import csv
from collections import defaultdict

from surprise import Dataset, Reader

from BaseDatasetLoader import BaseDatasetLoader


class AnimeListLoader(BaseDatasetLoader):
    """
    用于加载和处理数据集。
    """

    # itemID_to_name = {}
    # name_to_itemID = {}
    ratingsPath = './datasets/my-anime-list/reviews_processed.csv'
    itemsPath = './datasets/my-anime-list/animes.csv'

    def __init__(self, max_rows: int = 10000):
        self.max_rows = max_rows

    def load_dataset(self) -> Dataset:
        """
        加载最新小数据集，并编码为item名和itemID 的对应字典。

        :return: 包含用户评分数据的 Surprise 数据集。
        """

        # self.itemID_to_name = {}
        # self.name_to_itemID = {}

        with open(self.itemsPath, newline='', encoding='ISO-8859-1') as csvfile:
            itemsReader = csv.reader(csvfile)
            next(itemsReader)  # 跳过表头
            for row in itemsReader:
                itemID = int(row[0])
                itemName = row[1]
                self.id_to_name[itemID] = itemName
                self.name_to_id[itemName] = itemID

        # 加载评分数据
        max_rows = self.max_rows
        with open(self.ratingsPath, newline='') as csvfile:
            ratingReader = csv.reader(csvfile)
            next(ratingReader)  # 跳过表头
            limited_rows = []
            for i, row in enumerate(ratingReader):
                if max_rows is not None and i >= max_rows:
                    break
                limited_rows.append(row)

        # 将有限的数据写入临时文件并加载
        import tempfile
        with tempfile.NamedTemporaryFile(mode='w+', newline='', delete=False) as temp_file:
            writer = csv.writer(temp_file)
            writer.writerow(['user', 'item', 'rating'])  # 写入表头
            writer.writerows(limited_rows)
            temp_file_path = temp_file.name

        reader = Reader(
            line_format='user item rating',
            sep=',', skip_lines=1,
        )
        ratingsDataset = Dataset.load_from_file(temp_file_path, reader=reader)

        return ratingsDataset

    def get_user_ratings(self, user: int) -> list:
        """
        获取指定用户的评分数据。

        :param user: 用户ID。
        :return: 包含用户评分的列表，每个元素为 (itemID, rating)。
        """
        userRatings = []
        with open(self.ratingsPath, newline='') as csvfile:
            ratingReader = csv.reader(csvfile)
            next(ratingReader)
            for row in ratingReader:
                userID = int(row[0])
                # 找到当前用户的评分数据
                if user == userID:
                    movieID = int(row[1])
                    rating = float(row[2])
                    userRatings.append((movieID, rating))

        return userRatings

    def get_item_features(self) -> defaultdict:
        pass

    def get_popularity_ranks(self) -> defaultdict:

        '''
        获取物品的流行度排名
        :return: itemID到流行度排名的字典 {itemID: popularRank}
        '''
        ratings = defaultdict(int)  # [(itemID, ratingCount)...]
        rankings = defaultdict(int)  # [(itemID, popularRank)...]
        # 打开记录评分的csv, cols: [profile_id, anime_uid, score]
        with open(self.ratingsPath, newline='') as csvfile:
            ratingReader = csv.reader(csvfile)
            next(ratingReader)  # 跳过表头行
            for row in ratingReader:
                itemID = int(row[1])  # 获取物品id
                ratings[itemID] += 1  # 统计每个物品的评分次数

        rank = 1  # 排名 1, 2, 3, ...
        # 根据评分次数降序排序，生成排名
        # 根据 rating[1](item出现次数) 排序, 然后遍历, 将出现次数改为 rank(排名)
        for itemID, _ in sorted(ratings.items(), key=lambda x: x[1], reverse=True):
            rankings[itemID] = rank  # 设置排名
            rank += 1  # 下一个的排名变为当前rank+1
        return rankings

    def get_popularity_itemIDs(self):
        '''
        返回热度最高的物品列表
        :return:
        '''
        self.ratings = []
        score = defaultdict(int)
        ratings = defaultdict(int)  # [(itemID, ratingCount)...]
        # 打开记录评分的csv, cols: [profile_id, anime_uid, score]
        with open(self.ratingsPath, newline='') as csvfile:
            ratingReader = csv.reader(csvfile)
            next(ratingReader)  # 跳过表头行
            for row in ratingReader:
                itemID = int(row[1])  # 获取物品id
                ratings[itemID] += 1  # 统计每个物品的评分次数
                # 如果还没记录评分, 就直接记录
                if itemID not in score:
                    score[itemID] = int(row[2])
                # 如果这个评分更高, 就记录该评分 (只记录最高的评分)
                else:
                    if int(row[2]) > score[itemID]:
                        score[itemID] = int(row[2])  # 记录评分

        popular_item_ids = []  # [(itmeID, rating), ...]
        for itemID, _ in sorted(ratings.items(), key=lambda x: x[1], reverse=True):
            # 记录排名从高到低的item, 供SampleTopNRecs冷启动使用
            popular_item_ids.append([itemID, score[itemID]])
        return popular_item_ids

    # def getItemName(self, itemID: int) -> str:
    #     """
    #     获取指定itemID对应的name。
    #
    #     :param itemID: 项目ID
    #     :return: name
    #     """
    #     if itemID in self.itemID_to_name:
    #         return self.itemID_to_name[itemID]
    #     else:
    #         return ""
    #
    # def getItemID(self, name: str) -> int:
    #     """
    #     获取指定name对应的itemID。
    #
    #     :param name: 项目名称。
    #     :return: 项目ID。
    #     """
    #     if name in self.name_to_itemID:
    #         return self.name_to_itemID[name]
    #     else:
    #         return 0
